package pl.edu.agh.neurony.logic;

import java.util.List;

import pl.edu.agh.neurony.exceptions.BadNeuronInitiationException;
import pl.edu.agh.neurony.exceptions.LayerConstructionException;
import pl.edu.agh.neurony.exceptions.LearningException;

public class GrossbergLayer extends Layer {

	private Double momentum, basicMomentum;

	public GrossbergLayer(int layerNumber, Neuron neuron) throws LayerConstructionException {
		super(layerNumber, neuron);
	}

	public void learn(int step, int steps) throws BadNeuronInitiationException {
		for (Neuron neuron : neurons) {
			double deltaWeight = learningFactor * neuron.error * neuron.activationFunction.derivative(neuron.input);
			for (Connection connection : neuron.getInputConnections()) {
				double weightChange = deltaWeight * connection.getInputNeuron().output 
						+ momentum * connection.previousChange;
				connection.previousChange = weightChange;
				connection.setWeight(connection.getWeight() + weightChange);
			}
			double biasChange = deltaWeight * neuron.bias * neuron.BIAS + momentum * neuron.previousBiasChange;
			neuron.previousBiasChange = biasChange;
			neuron.bias += biasChange;
		}

		updateLearningFactor(step, steps);

		List<Connection> connections = neurons.get(0).getOutputConnections();
		if (connections.size() > 0) {
			int layerNr = connections.get(0).getOutputNeuron().layerNr;
			Layer layer = Net.getInstance().layersMap.get(layerNr);
			if (layer != null)
				layer.learn(step, steps);
		}
	}

	@Override
	public void updateLearningFactor(int step, int steps) {
		super.updateLearningFactor(step, steps);
		momentum = basicMomentum - ((double) step * basicMomentum / (double) steps);
	}
	
	public void propagateErrorBackward() throws LearningException {
		for (Neuron neuron : neurons) {
			neuron.error = 0.0;
			for (Connection connection : neuron.getOutputConnections()) {
				neuron.error += connection.getWeight() * connection.getOutputNeuron().error;
			}
			neuron.error *= neuron.activationFunction.derivative(neuron.input);
		}
		Neuron neuron = neurons.get(0).getInputConnections().get(0).getInputNeuron();
		GrossbergLayer layer = (GrossbergLayer) Net.getInstance().layersMap.get(neuron.layerNr);
		if (layer != null)
			layer.propagateErrorBackward();
	}

	@Override
	public void setLearningParams(Number... arguments) {
		basicLearningFactor = (Double) arguments[0];
		learningFactor = (Double) arguments[0];
		momentum = (Double) arguments[1];
		basicMomentum = (Double) arguments[1];
	}
}
